using System;
using System.Linq;
using System.Collections.Generic;

namespace Caramel.Linq
{
	public static class EnumerableExtensions
	{
		public static IEnumerable<T> Each<T>(this IEnumerable<T> items, Action<T> action)
		{
			if (items != null)
			{
				foreach (T item in items)
				{
					action(item);
				}
			}

			return items;
		}

		public static bool None<T>(this IEnumerable<T> items)
		{
			return !items.Any();
		}

		public static bool None<T>(this IEnumerable<T> items, Func<T, bool> predicate)
		{
			return !items.Any(predicate);
		}

		public static bool Contains<T>(this IEnumerable<T> source, T value, Func<T, T, bool> comparer)
		{
			return
				source.
					Contains(
						value,
						new DelegateComparer<T>(comparer)
					);
		}

		public static IEnumerable<T> Distinct<T, TProperty>(this IEnumerable<T> source, Func<T, TProperty> selector)
		{
			return source.Distinct(new MemberComparer<T, TProperty>(selector));
		}

		public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source, Func<T, T, bool> comparer)
		{
		    return source.Distinct(new DelegateComparer<T>(comparer));
		}

		public static IEnumerable<T> Except<T>(this IEnumerable<T> first, IEnumerable<T> second, Func<T, T, bool> comparer)
		{
			return
				first.
					Except(
						second,
						new DelegateComparer<T>(comparer)
					);
		}

		public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, TKey, bool> comparer)
		{
			return
				source.
					GroupBy(
						keySelector,
						new DelegateComparer<TKey>(comparer)
					);
		}

		public static IEnumerable<T> Intersect<T>(this IEnumerable<T> first, IEnumerable<T> second, Func<T, T, bool> comparer)
		{
			return
				first.
					Intersect(
						second,
						new DelegateComparer<T>(comparer)
					);
		}

		public static bool SequenceEqual<T>(this IEnumerable<T> first, IEnumerable<T> second, Func<T, T, bool> comparer)
		{
			return
				first.
					SequenceEqual(
						second,
						new DelegateComparer<T>(comparer)
					);
		}

		public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, TKey, bool> comparer)
		{
			return
				source.
					ToDictionary(
						keySelector,
						new DelegateComparer<TKey>(comparer)
					);
		}

		public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, TKey, bool> comparer)
		{
			return
				source.
					ToDictionary(
						keySelector,
						elementSelector,
						new DelegateComparer<TKey>(comparer)
					);
		}

		public static ILookup<TKey, TSource> ToLookup<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, TKey, bool> comparer)
		{
			return
				source.
					ToLookup(
						keySelector,
						new DelegateComparer<TKey>(comparer)
					);
		}

		public static ILookup<TKey, TElement> ToLookup<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, TKey, bool> comparer)
		{
			return
				source.
					ToLookup(
						keySelector,
						elementSelector,
						new DelegateComparer<TKey>(comparer)
					);
		}

		public static IEnumerable<T> Union<T>(this IEnumerable<T> first, IEnumerable<T> second, Func<T, T, bool> comparer)
		{
			return
				first.
					Union(
						second,
						new DelegateComparer<T>(comparer)
					);
		}
	}
}
